Function module ZSTB_DS_HTTP_CALLBACK

Import
I_DATASOURCE        TYPE  ROOSOURCER        OPTIONAL
I_CHABASNM          TYPE  RSCHABASNM        OPTIONAL
I_UPDMODE           TYPE  RSUPDMODE         OPTIONAL
I_FIELD_SEPARATOR   TYPE  ADD_SERVICE       DEFAULT '|'
I_CRLF              TYPE  C                 DEFAULT '/'
I_RFC_DEST          TYPE  RFCDES-RFCDEST    DEFAULT 'SAPHTTPA'
I_CALLBACK_URL      TYPE  EPM_URL           OPTIONAL
I_PARENT_SESSION_ID TYPE  CHAR255           OPTIONAL
I_STRING_QUALIFIER  TYPE  C1                DEFAULT ''

Export
SUBRC	LIKE	SYST-SUBRC

Tables
I_T_SELECT	LIKE	RSSELECT
I_T_FIELDS	LIKE	RSFIELDSEL
I_T_DATA
C_T_MESSAGES	LIKE	BALMI

Source Code
function zstb_ds_http_callback.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(I_DATASOURCE) TYPE  ROOSOURCER OPTIONAL
*"     VALUE(I_CHABASNM) TYPE  RSCHABASNM OPTIONAL
*"     VALUE(I_UPDMODE) TYPE  RSUPDMODE OPTIONAL
*"     VALUE(I_FIELD_SEPARATOR) TYPE  ADD_SERVICE DEFAULT '|'
*"     VALUE(I_CRLF) TYPE  C DEFAULT '/'
*"     VALUE(I_RFC_DEST) TYPE  RFCDES-RFCDEST DEFAULT 'SAPHTTPA'
*"     VALUE(I_CALLBACK_URL) TYPE  EPM_URL OPTIONAL
*"     VALUE(I_PARENT_SESSION_ID) TYPE  CHAR255 OPTIONAL
*"     VALUE(I_STRING_QUALIFIER) TYPE  C1 DEFAULT ''
*"  EXPORTING
*"     VALUE(SUBRC) LIKE  SYST-SUBRC
*"  TABLES
*"      I_T_SELECT STRUCTURE  RSSELECT OPTIONAL
*"      I_T_FIELDS STRUCTURE  RSFIELDSEL OPTIONAL
*"      I_T_DATA OPTIONAL
*"      C_T_MESSAGES STRUCTURE  BALMI OPTIONAL
*"----------------------------------------------------------------------
*-- Local Variables

  data : url_char         type c length 255.
*         value 'http://192.168.106.1:42200/rest/StambiaDeliveryService/2/receivedata?id=10'.
  data : data_length      type i value '5000',
         lv_stat_text(10) type c,
         lv_status(80)    type c.

  data : lt_table  type table of char255 with header line,
         lt_table2 type table of char255 with header line,
         lt_table3 type table of char255 with header line,
         lt_table4 type table of zstb_ds_table with header line
         .

  data : go_struct type ref to cl_abap_structdescr,
         gt_comp   type abap_component_tab,
         gs_comp   type abap_componentdescr.

  data : wf_ref    type ref to data,
         wf_line   type ref to data.
  data : vl_field  type rsfieldsel.

  data: vl_lines        type i,
        vl_index(8)     type n,
        vl_seqnr(6)     type n,
        vl_length       type i,
        vl_idfield(16)  type c,
        returnrowstring type string,
        datafieldstring type string,
        dataline        like lt_table4,
        double_string_qualifier(2) type c,
        callback_error(255)  type c.

*-- Field Symbols
  field-symbols:  <f_src>     type standard table,
                  <datafield> type any,
                  <datarow>   type any,
                  <fs_line>   type any.

  concatenate i_string_qualifier i_string_qualifier into double_string_qualifier.

* create a working temporary table to store data-source (DS) content
* gv_ds_table : the working table
* we 1st check storage table does not exist
* if so we create it with the same introspection method used before in "zstb_rfc_read_data_source" (refer to it for more technicel details)
  if <gv_ds_table> is not assigned.
    data wa_source type roosource.
    select single * into wa_source
      from roosource
      where oltpsource = i_datasource.
    create data wf_ref type table of (wa_source-exstruct). "Dynamic Itab
    assign wf_ref->* to <gv_ds_table>.
    create data wf_line like line of <gv_ds_table>. "Dynamic Work area
  endif.

* now storage table exists, we can extract DS data
  if <gv_ds_table> is assigned.
* confirm call back URL exists
    check i_callback_url is not initial.
    clear url_char.
    url_char = i_callback_url.
* make sure working table is a exact copy of fetched input DS content
    clear <gv_ds_table>.
    append lines of i_t_data to <gv_ds_table>.
* use the 1st line of working table to get a description of DS content
* fs_line : the first data line
* go_struct : the full description returned object
* cl_abap_typedescr : a collection of functions to get data types
*                     describe_by_data : one of this specific function
* gt_comp : description of each DS fields
*           vl_lines : number of fields
    read table <gv_ds_table> assigning <fs_line> index 1.
    if <fs_line> is assigned.
      go_struct ?= cl_abap_typedescr=>describe_by_data( <fs_line> ).
      gt_comp = go_struct->get_components( ).
      describe table gt_comp lines vl_lines.
    endif.
* fill DS data with CSV format
* datarow : current DS line
* dataline : a structure to store a CSV formatted DS line with is line number
* lt_table4 : a table of dataline (ie a draft of final CSV file)
* for each datra line ...
    loop at <gv_ds_table> assigning <datarow>.
      clear dataline.
      vl_index = vl_index + 1.
* ... and each data field, append 1-by-1 to create CSV file
      do vl_lines times.
        assign component sy-index of structure <datarow> to <datafield>.
        datafieldstring = <datafield>.
*       manage string qualification (if required)
*       see : https://tools.ietf.org/html/rfc4180
*             https://blogs.sap.com/2014/09/20/abap-and-line-feeds/
        if i_string_qualifier is not initial.
*         if data field contains string qualifier (ie contains ") ...
          if datafieldstring cs i_string_qualifier.
*           ... then double string qualifier (ie " become "")
            replace all occurrences of i_string_qualifier in datafieldstring with double_string_qualifier.
*           and surround data field with string delimiters
            concatenate i_string_qualifier datafieldstring i_string_qualifier into datafieldstring.
*         else, if ata field contains field delimiter (ie |) or line separator (ie CRLF)
          else.
            if ( datafieldstring cs i_field_separator )
              or ( datafieldstring cs cl_abap_char_utilities=>newline )
            .
*             surround data field with string delimiters
              concatenate i_string_qualifier datafieldstring i_string_qualifier into datafieldstring.
            endif.
          endif.
        endif.
*       concatenate fields to build current line
        if returnrowstring is initial and sy-index = 1.
          concatenate returnrowstring datafieldstring
                 into returnrowstring.
        else.
          concatenate returnrowstring i_field_separator datafieldstring
                 into returnrowstring.
        endif.
      enddo.
* when a DS line is CSV formatted, store it to CSV table "lt_table4"
      if returnrowstring is not initial.
        concatenate i_field_separator returnrowstring
               into returnrowstring.
        dataline-fieldsrow = returnrowstring.
        dataline-seqnr = vl_index.
        insert dataline into table lt_table4.
        clear returnrowstring.
      endif.
    endloop.

* now CSV file is created, we can send it back to Stambia using HTTP
* 1st, we complete URL to add sequence number (in order to concat final CSV file in order)
* HTTP_POST : internal SAP function to use HTTP protocol
*             see : https://www.sapdatasheet.org/abap/func/http_post.html
*             see : https://www.se80.co.uk/sapfms/h/http/http_post.htm
    gv_ds_seq  = gv_ds_seq + 1.
    vl_seqnr = gv_ds_seq.
    concatenate url_char '&'
                'seq=' vl_seqnr into url_char.
    condense url_char no-gaps.
*-- Set request_headers
    lt_table3 = 'X-keepInputMessage: no'.
    append lt_table3.
    if i_parent_session_id is not initial.
       concatenate 'X-parentSessionId:' i_parent_session_id into lt_table3.
       append lt_table3.
     endif.

    concatenate 'X-sessionName: receive data' vl_seqnr into lt_table3  separated by space.
    append lt_table3.
*-- Send Post
    call function 'HTTP_POST'
      exporting
        absolute_uri                      = url_char
        rfc_destination                   = i_rfc_dest
        blankstocrlf                      = i_crlf
      importing
        status_code                       = lv_status
        status_text                       = lv_stat_text
        response_entity_body_length       = data_length
      tables
        request_entity_body               = lt_table4
        response_entity_body              = lt_table
        response_headers                  = lt_table2
        request_headers                   = lt_table3
     exceptions
       connect_failed                    = 1
       timeout                           = 2
       internal_error                    = 3
       tcpip_error                       = 4
       system_failure                    = 5
       communication_failure             = 6
       others                            = 7
              .

**** end of functionnal business ****
* following code is just technical stuff : error management & data cleaning

    if sy-subrc <> 0.
*       message id sy-msgid type sy-msgty number sy-msgno
*              with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
      concatenate sy-msgid sy-msgno sy-msgv1 into callback_error separated by space.
      export callback_error to memory id 'ZSTB_DS_EXPORT_ERROR'.

    endif.

*-- End Insert

    free memory id 'RSFH_DATA'.
*    export <gv_ds_table> to memory id 'RSFH_DATA'.
  endif.

endfunction.
*INCLUDE bdcrecxy .